package step

import (
	"errors"
	"fmt"
	"github.com/wanchain/go-mpc/common/hexutil"
	"github.com/wanchain/go-mpc/log"
	"github.com/wanchain/go-mpc/p2p/discover"
	"github.com/wanchain/go-mpc/storeman/osmconf"
	"github.com/wanchain/go-mpc/storeman/schnorrcomm"
	mpcprotocol "github.com/wanchain/go-mpc/storeman/storemanmpc/protocol"
	"math/big"
	"strconv"
)

type mpcSGeneratorUni struct {
	BaseGenerator
	seed        big.Int
	message     map[discover.NodeID]big.Int
	result      big.Int
	preValueKey string
	grpIdString string
	smpcer      mpcprotocol.SchnorrMPCer
	ContextId   uint64
	sigIndex    int
}

func createSGeneratorUni(sigIndex int, smpcer mpcprotocol.SchnorrMPCer) *mpcSGeneratorUni {
	return &mpcSGeneratorUni{message: make(map[discover.NodeID]big.Int), smpcer: smpcer, sigIndex: sigIndex}
}

func (msg *mpcSGeneratorUni) initialize(totalNum int, result mpcprotocol.MpcResultInterface) error {
	msg.SetContextId(result.GetMpcContextId())
	log.SyslogDebug("mpcSGeneratorUni::initialize begin", "ctx", msg.GetContextId())

	key := mpcprotocol.RPk + strconv.Itoa(msg.sigIndex)
	rgpkBytes, err := result.GetByteValue(key)
	if err != nil {
		log.SyslogErr("mpcSGeneratorUni::initialize get RPk fail", "ctx", msg.GetContextId())
		return err
	}
	smpcer := msg.smpcer

	algBytes := msg.GetMpcContext().GetInput().Alg
	alg := int(big.NewInt(0).SetBytes(algBytes).Uint64())
	hashMBytes := msg.GetMpcContext().GetInput().HashData[msg.sigIndex]

	curveIdBytes := msg.GetMpcContext().GetInput().Curve
	curveId := int(big.NewInt(0).SetBytes(curveIdBytes).Uint64())

	var m *big.Int
	var gskShare, gskShareInv, rskShare, rskShareInv []big.Int
	var gpkShare, rpkShare mpcprotocol.CurvePointer

	var rskShareUsed, gskShareUsed *big.Int

	gpkBytes := msg.GetMpcContext().GetInput().PKBytes

	// rskShare
	key = mpcprotocol.RSkShare + strconv.Itoa(msg.sigIndex)
	rskShare, err = result.GetValue(key)
	if err != nil {
		log.SyslogErr("mpcSGeneratorUni::initialize get RSkShare fail", "ctx", msg.GetContextId())
		return err
	}

	if alg == mpcprotocol.ALGSCHNORR340 {
		key = mpcprotocol.RSkShareInv + strconv.Itoa(msg.sigIndex)
		rskShareInv, err = result.GetValue(key)
		if err != nil {
			log.SyslogErr("mpcSGeneratorUni::initialize get RSkShareInv fail", "ctx", msg.GetContextId())
			return err
		}
	}

	// gskShare
	gskShare, err = result.GetValue(mpcprotocol.MpcPrivateShare)
	if err != nil {
		log.SyslogErr("mpcSGeneratorUni::initialize get MpcPrivateShare fail", "ctx", msg.GetContextId())
		return err
	}

	if alg == mpcprotocol.ALGSCHNORR340 {
		gskShareInv, err = result.GetValue(mpcprotocol.MpcPrivateShareInverse)
		if err != nil {
			log.SyslogErr("mpcSGeneratorUni::initialize get MpcPrivateShareInverse fail", "ctx", msg.GetContextId())
			return err
		}
	}

	rskShareUsed = &rskShare[0]
	gskShareUsed = &gskShare[0]
	if alg == mpcprotocol.ALGSCHNORR {
		if curveId == mpcprotocol.SK256Curve {
			m, _ = schnorrcomm.GetSchnorrSecE(hashMBytes, rgpkBytes, gpkBytes, smpcer.GetMod())
		} else {
			m, _ = schnorrcomm.GetSchnorrE(hashMBytes, rgpkBytes, smpcer.GetMod())
		}

	} else if alg == mpcprotocol.ALGSCHNORR340 {
		xbytesRGPK, err := smpcer.XBytesByBytes(rgpkBytes)
		if err != nil {
			return err
		}
		xbytesGPK, err := smpcer.XBytesByBytes(gpkBytes)
		if err != nil {
			return err
		}

		m, _ = schnorrcomm.GetSchnorr340E(hashMBytes, xbytesRGPK, xbytesGPK, smpcer.GetMod())
		ptRpk, err := smpcer.UnMarshPt(rgpkBytes)
		if err != nil {
			return err
		}
		isOdd, err := smpcer.IsOdd(ptRpk)
		if err != nil {
			return err
		}

		if isOdd {
			rskShareUsed = &rskShareInv[0]
		}

		ptGpk, err := smpcer.UnMarshPt(gpkBytes)
		if err != nil {
			return err
		}
		isOdd, err = smpcer.IsOdd(ptGpk)
		if err != nil {
			return err
		}
		if isOdd {
			gskShareUsed = &gskShareInv[0]
		}

	}

	// malice code begin (just for test)
	//gskShare[0] = *schnorrcomm.BigOne
	// malice code end  (just for test)
	sigShare := smpcer.SchnorrSign(*gskShareUsed, *rskShareUsed, *m)
	msg.seed = sigShare

	rpkShare, err = smpcer.SkG(&rskShare[0])
	if err != nil {
		log.SyslogErr("mpcSGeneratorUni::initialize get MpcPrivateShare fail", "SkG rskShare error", err.Error(), "ctx", msg.GetContextId())
		return err
	}
	gpkShare, err = smpcer.SkG(&gskShare[0])
	if err != nil {
		log.SyslogErr("mpcSGeneratorUni::initialize get MpcPrivateShare fail", "SkG gskShare error", err.Error(), "ctx", msg.GetContextId())
		return err
	}

	log.Info(" mpcSGeneratorUni::initialize SchnorrSign ",
		"M", hexutil.Encode(hashMBytes),
		"m", hexutil.Encode(m.Bytes()),
		"gpkShare", smpcer.PtToHexString(gpkShare),
		"rpkShare", smpcer.PtToHexString(rpkShare), "ctx", msg.GetContextId())
	_, grpIdString, _ := osmconf.GetGrpId(result)

	msg.grpIdString = grpIdString

	log.SyslogDebug("mpcSGeneratorUni::initialize succeed", "ctx", msg.GetContextId())
	return nil
}

func (msg *mpcSGeneratorUni) calculateResult() error {
	log.SyslogDebug("mpcSGeneratorUni::calculateResult begin", "ctx", msg.GetContextId())
	var recieveIndex []uint16
	// x
	seeds := make([]big.Int, 0)
	sigshares := make([]big.Int, 0)
	for nodeId, value := range msg.message {
		xValue, err := osmconf.GetOsmConf().GetXValueByNodeId(msg.grpIdString, &nodeId, msg.smpcer)
		if err != nil {
			log.SyslogErr("mpcSGeneratorUni::calculateResult", "calculateResult.GetXValueByNodeId", err.Error(), "ctx", msg.GetContextId())
		}

		seeds = append(seeds, *xValue)
		sigshares = append(sigshares, value)
		tempIndex, err := osmconf.GetOsmConf().GetInxByNodeId(msg.grpIdString, &nodeId)
		recieveIndex = append(recieveIndex, tempIndex)
	}

	threshold, _ := osmconf.GetOsmConf().GetThresholdNum(msg.grpIdString)
	if threshold < uint16(1) {
		log.SyslogErr("mpcSGeneratorUni::calculateResult threshold is lesser 1", "ctx", msg.GetContextId())
		return errors.New("mpcSGeneratorUni::calculateResult threshold is lesser 1")
	}
	degree := threshold - 1

	allIndex, _ := osmconf.GetOsmConf().GetGrpElemsInxes(msg.grpIdString)
	losingIndex := osmconf.Difference(*allIndex, recieveIndex)

	log.SyslogInfo("mpcSGeneratorUni::calculateResult all signature share",
		"Need nodes number:", threshold,
		"Now nodes number:", len(sigshares), "ctx", msg.GetContextId())
	if len(sigshares) < int(threshold) {
		//return mpcprotocol.ErrSNW
		return errors.New(fmt.Sprintf("%s %s", mpcprotocol.ErrTooLessDataCollected.Error(), osmconf.GetOsmConf().BuildLosingByIndex(msg.grpIdString, losingIndex)))
	}

	result := msg.smpcer.Lagrange(sigshares, seeds[:], int(degree))
	msg.result = result
	log.SyslogInfo("mpcSGeneratorUni::calculateResult  succeed", "ctx", msg.GetContextId())

	return nil
}

func (msg *mpcSGeneratorUni) SetSchnorrMpcer(smcer mpcprotocol.SchnorrMPCer) error {
	msg.smpcer = smcer
	return nil
}

func (msg *mpcSGeneratorUni) SetContextId(contextId uint64) {
	msg.ContextId = contextId
}

func (msg *mpcSGeneratorUni) GetContextId() uint64 {
	return msg.ContextId
}

func (msg *mpcSGeneratorUni) SetMpcContext(mpc MpcContextFunc) {
	msg.mpc = mpc
}

func (msg *mpcSGeneratorUni) GetMpcContext() MpcContextFunc {
	return msg.mpc
}
